// Copyright 2023 The Ryan SU Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package jwt

import (
	"github.com/golang-jwt/jwt/v4"
)

type JWTClaims struct {
	Exp      int64  `json:"exp"`
	Iat      int64  `json:"iat"`
	UserId   string `json:"userId"`
	TenantId string `json:"tenantId"`
	RoleIds  string `json:"roleIds"`
}

type Option struct {
	f func(*JWTClaims)
}

func WithExp(exp int64) Option {
	return Option{func(claims *JWTClaims) {
		claims.Exp = exp
	}}
}

func WithIat(iat int64) Option {
	return Option{func(claims *JWTClaims) {
		claims.Iat = iat
	}}
}

func WithUserId(userId string) Option {
	return Option{func(claims *JWTClaims) {
		claims.UserId = userId
	}}
}

func WithTenantId(tenantId string) Option {
	return Option{func(claims *JWTClaims) {
		claims.TenantId = tenantId
	}}
}

func WithRoleIds(roleIds string) Option {
	return Option{func(claims *JWTClaims) {
		claims.RoleIds = roleIds
	}}
}

func NewJwtTokenWithOption(secretKey string, opts ...Option) (string, error) {
	claims := &JWTClaims{}
	for _, opt := range opts {
		opt.f(claims)
	}

	genClaims := make(jwt.MapClaims)
	if claims.Exp > 0 {
		genClaims["exp"] = claims.Exp
	}
	if claims.Iat > 0 {
		genClaims["iat"] = claims.Iat
	}
	if len(claims.UserId) > 0 {
		genClaims["userId"] = claims.UserId
	}
	if len(claims.TenantId) > 0 {
		genClaims["tenantId"] = claims.TenantId
	}
	if len(claims.RoleIds) > 0 {
		genClaims["roleIds"] = claims.RoleIds
	}

	token := jwt.NewWithClaims(jwt.SigningMethodHS256, genClaims)
	return token.SignedString([]byte(secretKey))
}

func NewJwtToken(secretKey string, uid, tenantId string, iat, seconds int64) (string, error) {
	claims := make(jwt.MapClaims)
	claims["exp"] = iat + seconds
	claims["iat"] = iat
	claims["userId"] = uid
	claims["tenantId"] = tenantId
	token := jwt.New(jwt.SigningMethodHS256)
	token.Claims = claims
	return token.SignedString([]byte(secretKey))
}
